/**
 *  Copyright (C) 2013  Piotr Szczepa�ski
 *
 *   This program is free software: you can redistribute it and/or modify
 *   it under the terms of the GNU General Public License as published by
 *   the Free Software Foundation, either version 3 of the License, or
 *   (at your option) any later version.
 *
 *   This program is distributed in the hope that it will be useful,
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *   GNU General Public License for more details.
 *
 *   You should have received a copy of the GNU General Public License
 *   along with this program.  If not, see <http://www.gnu.org/licenses/>.
*/
package edu.pw.elka.gtsna.test_performance;

import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.Executors;
import java.util.concurrent.Semaphore;
import java.util.concurrent.TimeUnit;

import edu.pw.elka.gtsna.centrality.betweenness.ProbabilisticSVNodeBetweenness;
import edu.pw.elka.gtsna.centrality.betweenness.SVNodeBetweenness;
import edu.pw.elka.gtsna.centrality.betweenness.SuccessiveGroupNodeBetweenness;
import edu.pw.elka.gtsna.graph.Graph;
import edu.pw.elka.gtsna.graph.UndirectedGraph;
import edu.pw.elka.gtsna.graph_algorithms.CheckConnectivity;
import edu.pw.elka.gtsna.graph_algorithms.FindComponents;
import edu.pw.elka.gtsna.graph_creator.ScaleFreeGraphGenerator;
import edu.pw.elka.gtsna.graph_creator.TGFReader;
import edu.pw.elka.gtsna.montecarlo.MonteCarloShapleyValue;
import edu.pw.elka.gtsna.ranking.Ranking;
import edu.pw.elka.gtsna.utilities.Agregator;
import edu.pw.elka.gtsna.utilities.Timer;
import edu.pw.elka.gtsna.utilities.MathPowers;

/**
 * @author Piotr Lech Szczepa�ski
 * @author P.Szczepanski@ii.pw.edu.pl 
 *
 */
public class SVBCPerformerRealData  extends Performer  {

	// exact time
	Agregator[] MCTimes = new Agregator[2001];

	
	int numberOfMC = 1;

	int maxIterations = 10000;
	int MCstep=10;
	
	//final Graph gr = new UndirectedGraph(new TGFReader("ego-facebook.tar"));
	//final Graph gr =  new UndirectedGraph(new ScaleFreeGraphGenerator(150,(int)Math.sqrt(50)));
	//MathPowers.precompute(gr.getNumberOfNodes());
	
	
    Graph[] gr = null;
	
	Map<Integer, Double> partialSolution = new HashMap<Integer, Double>();
    long partiaInterations = 0;
    long totalTime = 0;
	
	SVBCPerformerRealData(){
	}
	
	/**
	 * @param threads
	 */
	SVBCPerformerRealData(int threads) {
		super(threads);
		gr = new Graph[threads];
	
		for (int i= 0; i<threads; i++){
			gr[i] = new UndirectedGraph(new TGFReader("emails.in"));
		}
		
		FindComponents fc = new FindComponents(gr[0]);fc.compute();
		System.out.println(fc.getComponents());
		System.out.println(fc.getComponents().size());
		for (int a: gr[0].getAllNodes()){
			partialSolution.put(a, 0.0);
		}
	}

	synchronized static int index(double error){
		return (int) Math.min(Math.round(error*100),2000);
	}
	
	// MC_STEP, MC_ITERATIONS, NUMBER_OF_THREADS
	public static void main(String[] args) throws InterruptedException { 
		if (args.length < 3) {
			System.out.println("Missing arguments: "+"MC_STEP, MC_ITERATIONS, NUMBER_OF_THREADS");
			return;
		}

		
		SVBCPerformerRealData performer = new SVBCPerformerRealData(Integer.parseInt(args[2]));
		performer.setMCstep(Integer.parseInt(args[0]));
		performer.setMaxIterations(Integer.parseInt(args[1]));
		performer.runTest();
	}

	
	public void runTest() throws InterruptedException{
		System.out.println("running");

		System.out.println(gr[0].getAllNodes());
		System.out.println(gr[0].getNumOfEdges());
		
		for (int i=0; i<2001; i++){
			MCTimes[i]= new Agregator();
		}
		

		final Timer exactT = new Timer();	
		final Timer[] mcT = new Timer[NUMBER_OF_THREADS];
		
		MathPowers.precompute(gr[0].getNumOfNodes());
		
		final SVNodeBetweenness SV = new SVNodeBetweenness(gr[0]);
		exactT.start();
		SV.computeCentrality();
		exactT.stop();
		System.out.println("SV Exact: "+sixDForm.format(exactT.getMilliTime()));
		exactT.reset();

		Ranking<Integer> r = new Ranking(SV.getCentralities());
		r.printRanking();
		
		ProbabilisticSVNodeBetweenness PSV = new ProbabilisticSVNodeBetweenness(gr[0]);
		exactT.start();
		PSV.computeCentrality();
		exactT.stop();
		System.out.println("PSV Exact: "+sixDForm.format(exactT.getMilliTime()));
		exactT.reset();

		
		final SuccessiveGroupNodeBetweenness[] gS = new SuccessiveGroupNodeBetweenness[NUMBER_OF_THREADS];
		final Set<Integer> synset = new HashSet<Integer>();
		
		final Set<Integer> queue = Collections.synchronizedSet(synset);
		
		for (int i= 0; i<NUMBER_OF_THREADS; i++){
			final int  j = i;
			mcT[j] = new Timer();
			gS[j] = new SuccessiveGroupNodeBetweenness(gr[j]);
			executorService.execute(new Runnable() {
			    public void run() {
					gS[j].preProcess();
					System.out.println("Policzylo: "+j);
			    }
			});
			queue.add(j);
		}
		
		executorService.shutdown();
		
		try {
			executorService.awaitTermination(Long.MAX_VALUE, TimeUnit.SECONDS);
		} catch (InterruptedException e) {
			System.out.println("ERROR: PROCESS INTERUPTED");
		}
	
		executorService = Executors.newFixedThreadPool(Performer.NUMBER_OF_THREADS);
		
	    final Semaphore available = new Semaphore(NUMBER_OF_THREADS);
		
		for (int iter=0; iter<maxIterations; iter+=MCstep){
			available.acquire();

			executorService.execute(new Runnable() {
			    public void run() {
			    	int  j = getFree(queue);
			    	System.out.println("j TO "+j);
			    	MonteCarloShapleyValue MC = new MonteCarloShapleyValue(gS[j], MCstep);

    				mcT[j].start();
    				MC.computeSolution(MCstep);
    				mcT[j].stop();
    				
    				updateData(MC.getSolution(),MCstep,mcT[j].getMilliTime(),j);

    				
		    		mcT[j].reset();
		    		   
					//MCTimes[index(Ranking.relativeError(partialSolution(), SV.getCentralities()))].observe(getTotalTime());
					addFree(queue,j);
					synchronized(this) {
						System.out.println(index(Ranking.relativeError(partialSolution(j), SV.getCentralities()))+" "+sixDForm.format(getTotalTime()));
					}
					available.release();
			    }
			});

	    }
		executorService.shutdown();
		
		try {
				executorService.awaitTermination(Long.MAX_VALUE, TimeUnit.NANOSECONDS);
			} catch (InterruptedException e) {
				System.out.println("ERROR: PROCESS INTERUPTED");
			}
		
		


	}

	synchronized int getFree(Set<Integer> queue){
		int p = -1;
		for(Integer i : queue)
		{
		   p = i;
		   break;
		}
		queue.remove(p);
		return p;
	}
	
	synchronized void addFree(Set<Integer> queue, int j){
		queue.add(j);
	}
	
	
	synchronized void updateData(Map<Integer, Double> solution, int iter, long time,int j){
		rewriteCons(solution,j);
		increaseIterations(MCstep);
		increaseTime(time);
	}
	
	synchronized void rewriteCons(Map<Integer, Double> solution, int j){
		for (int a: gr[j].getAllNodes()){
			partialSolution.put(a, partialSolution.get(a)+solution.get(a));
		}	
	}

	synchronized void increaseIterations(int iter){
		partiaInterations+=iter;
	}

	synchronized void increaseTime(long time){
		totalTime+=time;
	}
	
	synchronized long getTotalTime(){
		return totalTime;
	}
	
	synchronized public Map<Integer, Double> partialSolution(int j){

		Map<Integer, Double> partialSV = new LinkedHashMap<Integer, Double>();
		for (int a: gr[j].getAllNodes()){
			if (partiaInterations!=0){
				partialSV.put(a, partialSolution.get(a)/(double)partiaInterations);
			} else {
				partialSV.put(a, 0.0);
			}
		}
		return partialSV;
	}
	
	
	public int getMaxIterations() {
		return maxIterations;
	}

	public void setMaxIterations(int maxIterations) {
		this.maxIterations = maxIterations;
	}

	public int getMCstep() {
		return MCstep;
	}

	public void setMCstep(int mCstep) {
		MCstep = mCstep;
	}
	


	
}
